package structure;

import io.Logger;

import java.util.ArrayList;

import utils.Param;

public class SympleBoard extends Board{
	
	final private Group[] groups;
	final private ArrayList<Group> lGroups;
	
	private boolean komiAvailable = true;

	public SympleBoard(byte[] config, Group[] groups, long hash) {
		super(config, hash);
		this.groups = groups;
		lGroups = new ArrayList<Group>();
	}
	
	public SympleBoard(int size) {
		super(size);
		this.groups = new Group[size*size];
		lGroups = new ArrayList<Group>();
	}

	@Override
	public float[] score() {
		float[] score = new float[2];
		
		for(Group c : lGroups){
			score[(c.getColor()+1)/2] = c.size()-6;
		}
		
		if(score[0] < 0)
			score[0] = 0;
		
		if(score[1] < 0)
			score[1] = 0;
		
		return score;
	}

	@Override
	public boolean isOver() {
		//TODO : Might be faster if we store the empty point
		for(int x = 0; x<config.length; x++)
				if(config[x] == 0)
					return false;
		return true;
	}

	@Override
	protected void move(Move move) {
		ArrayList<Coordinate> coords = move.getStones();
		ArrayList<Coordinate> nes;
		ArrayList<Group> toMerge = new ArrayList<Group>();
		Group group;
		
		Logger.logln(this.getClass(), move+" "+move.getStones().get(0).x+" "+move.getStones().get(0).y);
		
		for(Coordinate c : coords){
			config[Param.SIZE*c.y + c.x] = move.getColor();
			
			toMerge.clear();
			
			group = new Group();
			group.addStone(c);
			group.setLiberties(getLiberties(c));
			
			toMerge.add(group);
			
			nes = c.getNeighbours();
			
			for(Coordinate n : nes){
				if(config[Param.SIZE*n.y + n.x] == move.getColor() && toMerge.indexOf(groups[Param.SIZE*n.y + n.x]) == -1)
					toMerge.add(groups[Param.SIZE*n.y + n.x]);
				else if(config[Param.SIZE*n.y + n.x] == -1*move.getColor() && groups[Param.SIZE*n.y + n.x].isLibertyOf(n))
					groups[Param.SIZE*n.y + n.x].removeLiberty(n);
			}
			komiAvailable = komiAvailable & toMerge.size() == 1;
			Group.merge(toMerge, this);
			
		}
		
		
	}
	
	public void removeGroup(Group group){
		lGroups.remove(group);
	}
	
	public ArrayList<Coordinate> getLiberties(Coordinate c){
		ArrayList<Coordinate> nes = c.getNeighbours();
		ArrayList<Coordinate> liberties = new ArrayList<Coordinate>();
		for(Coordinate n : nes)
			if(config[Param.SIZE*n.y + n.x] == 0)
				liberties.add(n);
		
		return liberties;
				
	}

	@Override
	public ArrayList<Move> getValidMoves(byte color) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean isValid(Move move) {
		byte[] count = new byte[lGroups.size()];
		
		boolean flagLastMove = (nEmpty() - move.getStones().size()) == 0;
		boolean flagMoreOne = false;
		byte countExplor = 0;
		
		boolean isExplore;
		for(Coordinate c : move.getStones()){
			
			//Check if a move is played over an existing stone
			if(config[c.y*Param.SIZE+c.x] != 0)
				return false;
			
			isExplore = true;
			//Count the number of added stones per group
			for(int i = 0; i<lGroups.size(); i++){
				if(lGroups.get(i).getColor() == move.getColor() && lGroups.get(i).isLibertyOf(c)){
					count[i]++;
					flagMoreOne = flagMoreOne | count[i] > 1;
					isExplore = false;
				}
				
			}
			
			if(isExplore) countExplor++;
			//More than one explore move is not allowed
			if(countExplor>1) return false;
			
			//Exploration + expantion without komi available or playing as white is not allowed
			if(countExplor == 1 && move.getStones().size() > 1 && (move.getColor() == -1 || !komiAvailable))
				return false;
		}
		
		//Check that all the possible groups have exactly one more stone
		for(int i = 0; i<count.length; i++)
			if(lGroups.get(i).getColor() == move.getColor() && lGroups.get(i).getLiberties().size() > 0 && count[i] == 0)
				return false;
		
		
		
		//More than one and not the last filling situation
		if(flagMoreOne && !flagLastMove)
			return false;
		
		//TODO Check if it is the last filling situation
		
		
		return true;
	}
	
	public int nEmpty(){
		int count = 0;
		for(int i = 0; i<config.length; i++)
			if(config[i]==0)
				count++;
		
		return count;
	}

	public Group[] getGroups() {
		return groups;
	}
	
	public void addGroup(Group group) {
		lGroups.add(group);
	}
	
	public void setGroupAt(Group group, Coordinate c){
		groups[Param.SIZE*c.y + c.x] = group;
	}

	@Override
	public Board clone() {
		SympleBoard board = new SympleBoard(Param.SIZE);
		
		System.arraycopy(config, 0, board.config, 0, config.length);
		 
		for(Group group : this.lGroups){
			board.lGroups.add(group.cloneTo(board));
		}
		
		board.komiAvailable = komiAvailable;
		
		return board;
	}

	@Override
	public String toString() {
		String board = "";
		for(int y = 0; y<Param.SIZE; y++){
			for(int x = 0; x<Param.SIZE; x++){
				if(config[y*Param.SIZE+x] == 0)
					board += " .";
				if(config[y*Param.SIZE+x] == -1)
					board += " x";
				if(config[y*Param.SIZE+x] == 1)
					board += " o";
			}
			board += "\n";
		}
		board += "\n\n";
		
		boolean isLiberty = false;
		Coordinate tmp;
		for(int y = 0; y<Param.SIZE; y++){
			for(int x = 0; x<Param.SIZE; x++){
				if(config[y*Param.SIZE+x] == 0){
					tmp = new Coordinate((byte)x,(byte)y);
					isLiberty = false;
					for(Group group : lGroups){
						isLiberty = isLiberty || group.isLibertyOf(tmp);
					}
					if(isLiberty)
						board += " *";
					else
						board += " .";
				}else{
					board += " "+lGroups.indexOf(groups[y*Param.SIZE+x]);
				}
			}
			board += "\n";
		}
		
		return board;
	}
	
}
